package org.wysaid.view;

/**
 * Created by wangyang on 15/7/27.
 */


import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.media.ExifInterface;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;

import org.wysaid.camera.CameraInstance;
import org.wysaid.common.Common;
import org.wysaid.common.FrameBufferObject;
import org.wysaid.nativePort.CGEFrameRecorder;
import org.wysaid.nativePort.CGENativeLibrary;
import org.wysaid.texUtils.TextureRenderer;
import org.wysaid.texUtils.TextureRendererDrawOrigin;

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.IntBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

/**
 * Created by wangyang on 15/7/17.
 */
public class CameraGLSurfaceView extends GLSurfaceView implements GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener {

    public static final String LOG_TAG = Common.LOG_TAG;
    private static final int INIT_CAMERA = 0;
    protected final int[] mThunbnailLock = new int[0];
    public int maxTextureSize = 0;
    public int viewWidth;
    public int viewHeight;
    public int maxPreviewWidth = 1920;
    public int maxPreviewHeight = 480;
    public ClearColor clearColor;
    protected int mRecordWidth = 1920;
    protected int mRecordHeight = 480;
    protected SurfaceTexture mSurfaceTexture;
    protected int mTextureID;
    protected CGEFrameRecorder mFrameRecorder;
    protected Context mContext;
    protected TextureRenderer.Viewport mDrawViewport = new TextureRenderer.Viewport();
    protected boolean mIsUsingMask = false;
    protected boolean mFitFullView = false;
    protected float mMaskAspectRatio = 1.0f;
    protected float[] mTransformMatrix = new float[16];
    //是否使用后置摄像头
    protected boolean mIsCameraBackForward = true;
    protected OnCreateCallback mOnCreateCallback;
    protected long mTimeCount2 = 0;
    protected long mFramesCount2 = 0;
    protected long mLastTimestamp2 = 0;
    protected Bitmap mThunbnailBmp;
    protected TakeThunbnailCallback mTakeThunbnailCallback;
    protected int mThunbnailWidth, mThunbnailHeight;
    protected IntBuffer mThunbnailBuffer;
    TextureRendererDrawOrigin mBackgroundRenderer;
    int mBackgroundTexture;
    RectF mThumnailClipingArea;
    private DriveVideoHandler driveVideoHandler;

    CameraInstance.CameraOpenCallback cameraOpenCallback;

    public void setCameraOpenCallback(CameraInstance.CameraOpenCallback cameraOpenCallback) {
        this.cameraOpenCallback = cameraOpenCallback;
    }

    public CameraGLSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        Log.i(LOG_TAG, "MyGLSurfaceView Construct...");

        setEGLContextClientVersion(2);
        setEGLConfigChooser(8, 8, 8, 8, 8, 0);
        getHolder().setFormat(PixelFormat.RGBA_8888);
        setRenderer(this);
        setRenderMode(RENDERMODE_WHEN_DIRTY);
        setZOrderOnTop(false);
//        setZOrderMediaOverlay(true);

        clearColor = new ClearColor();
        mContext = context;

        driveVideoHandler = new DriveVideoHandler();
    }

    public CGEFrameRecorder getRecorder() {
        return mFrameRecorder;
    }

    //将对preview进行缩放。 使用默认值即可， 最好不要修改
    void setMaxPreviewSize(int w, int h) {
        maxPreviewWidth = w;
        maxPreviewHeight = h;
    }

    public void setFitFullView(boolean fit) {
        mFitFullView = fit;
        if (mFrameRecorder != null)
            calcViewport();
    }

    public boolean isUsingMask() {
        return mIsUsingMask;
    }

    public boolean isCameraBackForward() {
        return mIsCameraBackForward;
    }

    public void setClearColor(float r, float g, float b, float a) {
        clearColor.r = r;
        clearColor.g = g;
        clearColor.b = b;
        clearColor.a = a;
        queueEvent(new Runnable() {
            @Override
            public void run() {
                GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
                GLES20.glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
                GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
            }
        });
    }

    public CameraInstance cameraInstance() {
        return CameraInstance.getInstance();
    }

    //在onSurfaceCreated之前设置有效
    public void presetCameraForward(boolean isBackForward) {
        mIsCameraBackForward = isBackForward;
    }

    //注意， 录制的尺寸将影响preview的尺寸
    //这里的width和height表示竖屏尺寸
    //在onSurfaceCreated之前设置有效
    public void presetRecordingSize(int width, int height) {
        if (width > maxPreviewWidth || height > maxPreviewHeight) {
            float scaling = Math.min(maxPreviewWidth / (float) width, maxPreviewHeight / (float) height);
            width = (int) (width * scaling);
            height = (int) (height * scaling);
        }

        mRecordWidth = width;
        mRecordHeight = height;
        cameraInstance().setPreferPreviewSize(width, height);
    }

    public synchronized void switchCamera() {
        mIsCameraBackForward = !mIsCameraBackForward;

        if (mFrameRecorder != null) {
            queueEvent(new Runnable() {
                @Override
                public void run() {

                    if (mFrameRecorder == null) {
                        Log.e(LOG_TAG, "Error: switchCamera after release!!");
                        return;
                    }

                    cameraInstance().stopCamera();

                    int facing = mIsCameraBackForward ? Camera.CameraInfo.CAMERA_FACING_BACK : Camera.CameraInfo.CAMERA_FACING_FRONT;

                    mFrameRecorder.setSrcRotation((float) (Math.PI / 2.0));
                    mFrameRecorder.setRenderFlipScale(1.0f, -1.0f);

                    if (mIsUsingMask) {
                        mFrameRecorder.setMaskTextureRatio(mMaskAspectRatio);
                    }

                    cameraInstance().tryOpenCamera(new CameraInstance.CameraOpenCallback() {
                        @Override
                        public void cameraReady() {
                            if (!cameraInstance().isPreviewing()) {
                                Log.i(LOG_TAG, "## switch camera -- start preview...");
                                cameraInstance().startPreview(mSurfaceTexture);
                                mFrameRecorder.srcResize(cameraInstance().previewWidth(), cameraInstance().previewHeight());

                                if (cameraOpenCallback != null) {
                                    cameraOpenCallback.cameraReady();
                                }
                            }
                        }
                    }, facing);

//                    resumeDetectingFace();
                    requestRender();
                }
            });
        }
    }

//    protected boolean mIsDetectingFace = false;
//
//    public class FaceArea {
//        float x, y;
//        float gx, gy;
//    }
//
//    protected int[] mFaceAreaLock = new int[0];
//    public FaceArea mFaceArea;
////    public int mFaceCount = 0;
//
//    public boolean isDetectingFace() {
//        return mIsDetectingFace;
//    }
//
//    public synchronized boolean startDetectingFaceWithDefaultFilter() {
//
//        if(mIsDetectingFace) {
//            Log.e(LOG_TAG, "Detecting is started already!!");
//            return false;
//        }
//
//        int maxFaces;
//
//        try {
//
//            maxFaces = cameraInstance().getParams().getMaxNumDetectedFaces();
//
//            if(maxFaces <= 0) {
//                Log.e(LOG_TAG, "Device does not support face detection");
//                return false;
//            }
//
//            synchronized (mFaceAreaLock) {
//                mFaceArea = new FaceArea();
//            }
//
//            cameraInstance().getCameraDevice().setFaceDetectionListener(this);
//            cameraInstance().getCameraDevice().startFaceDetection();
//
//        } catch (Exception e) {
//            e.printStackTrace();
//            return false;
//        }
//
//        mIsDetectingFace = true;
//
//        queueEvent(new Runnable() {
//            @Override
//            public void run() {
//                if(mFrameRecorder != null)
//                    mFrameRecorder.enableFaceDetectWithDefaultFilter(true);
//            }
//        });
//        return true;
//    }
//
//    protected void resumeDetectingFace() {
//        if(mIsDetectingFace) {
//            try {
//                cameraInstance().getCameraDevice().setFaceDetectionListener(this);
//                cameraInstance().getCameraDevice().startFaceDetection();
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
//        }
//    }
//
//    public synchronized void stopDetectingFace() {
//        if(mIsDetectingFace) {
//            mIsDetectingFace = false;
//            cameraInstance().getCameraDevice().stopFaceDetection();
//            cameraInstance().getCameraDevice().setFaceDetectionListener(null);
//        }
//
//        synchronized (mFaceAreaLock) {
//            mFaceArea = null;
//        }
//
//        queueEvent(new Runnable() {
//            @Override
//            public void run() {
//                if(mFrameRecorder != null)
//                    mFrameRecorder.enableFaceDetectWithDefaultFilter(false);
//            }
//        });
//    }

//    public void onFaceDetection(Camera.Face[] faces, Camera camera) {
//
//        synchronized (mFaceAreaLock) {
//
//            if(mFaceArea != null) {
//
//                if(faces != null && faces.length > 0) {
//                    Camera.Face face = faces[0];
//                    mFaceArea.x = (face.rect.left + face.rect.right) / 4000.0f + 0.5f;
//                    mFaceArea.y = (face.rect.top + face.rect.bottom) / 4000.0f + 0.5f;
//                    mFaceArea.gx = (face.rect.width() + face.rect.height()) / 8000.0f;
//                    mFaceArea.gy = 1.4142f * mFaceArea.gx;
//
//                    queueEvent(new Runnable() {
//                        @Override
//                        public void run() {
//                            if(cameraInstance().getFacing() == Camera.CameraInfo.CAMERA_FACING_FRONT) {
//                                mFrameRecorder.setFaceArea(1.0f - mFaceArea.y, 1.0f - mFaceArea.x, mFaceArea.gx, mFaceArea.gy);
//                            } else {
//                                mFrameRecorder.setFaceArea(1.0f - mFaceArea.y, mFaceArea.x, mFaceArea.gx, mFaceArea.gy);
//                            }
//
//                        }
//                    });
//
//                }
//            }
//        }
//    }

    //注意， focusAtPoint 会强制 focus mode 为 FOCUS_MODE_AUTO
    //如果有自定义的focus mode， 请在 AutoFocusCallback 里面重设成所需的focus mode。
    //x,y 取值范围: [0, 1]， 一般为 touchEventPosition / viewSize.
    public void focusAtPoint(float x, float y, Camera.AutoFocusCallback focusCallback) {
        cameraInstance().focusAtPoint(y, 1.0f - x, focusCallback);
    }

    // 参数为
    //    Camera.Parameters.FLASH_MODE_AUTO;
    //    Camera.Parameters.FLASH_MODE_OFF;
    //    Camera.Parameters.FLASH_MODE_ON;
    //    Camera.Parameters.FLASH_MODE_RED_EYE
    //    Camera.Parameters.FLASH_MODE_TORCH 等
    public synchronized boolean setFlashLightMode(String mode) {

        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)) {
            Log.e(LOG_TAG, "当前设备不支持闪光灯!");
            return false;
        }

        if (!mIsCameraBackForward) {
            return false;
        }

        Camera.Parameters parameters = cameraInstance().getParams();

        if (parameters == null)
            return false;

        try {

            if (!parameters.getSupportedFlashModes().contains(mode)) {
                Log.e(LOG_TAG, "Invalid Flash Light Mode!!!");
                return false;
            }

            parameters.setFlashMode(mode);
            cameraInstance().setParams(parameters);
        } catch (Exception e) {
            Log.e(LOG_TAG, "修改闪光灯状态失败, 请检查是否正在使用前置摄像头?");
            return false;
        }

        return true;
    }

    public synchronized void setFilterWithConfig(final String config) {
        queueEvent(new Runnable() {
            @Override
            public void run() {

                if (mFrameRecorder != null) {
                    mFrameRecorder.setFilterWidthConfig(config);
                } else {
                    Log.e(LOG_TAG, "setFilterWithConfig after release!!");
                }
            }
        });
    }

    public void setFilterIntensity(final float intensity) {
        queueEvent(new Runnable() {
            @Override
            public void run() {
                if (mFrameRecorder != null) {
                    mFrameRecorder.setFilterIntensity(intensity);
                } else {
                    Log.e(LOG_TAG, "setFilterIntensity after release!!");
                }
            }
        });
    }

    public void setMaskBitmap(final Bitmap bmp, final boolean shouldRecycle) {
        setMaskBitmap(bmp, shouldRecycle, null);
    }

    //注意， 当传入的bmp为null时， SetMaskBitmapCallback 不会执行.
    public void setMaskBitmap(final Bitmap bmp, final boolean shouldRecycle, final SetMaskBitmapCallback callback) {

        queueEvent(new Runnable() {
            @Override
            public void run() {

                if (mFrameRecorder == null) {
                    Log.e(LOG_TAG, "setMaskBitmap after release!!");
                    return;
                }

                if (bmp == null) {
                    mFrameRecorder.setMaskTexture(0, 1.0f);
                    mIsUsingMask = false;
                    calcViewport();
                    return;
                }

                int texID = Common.genNormalTextureID(bmp, GLES20.GL_NEAREST, GLES20.GL_CLAMP_TO_EDGE);

                mFrameRecorder.setMaskTexture(texID, bmp.getWidth() / (float) bmp.getHeight());
                mIsUsingMask = true;
                mMaskAspectRatio = bmp.getWidth() / (float) bmp.getHeight();

                if (callback != null) {
                    callback.setMaskOK(mFrameRecorder);
                }

                if (shouldRecycle)
                    bmp.recycle();

                calcViewport();
            }
        });
    }

    public void setBackgroundImage(final Bitmap bmp, final boolean shouldRecycle, final SetBackgroundImageCallback callback) {

        queueEvent(new Runnable() {
            @Override
            public void run() {

                if (mFrameRecorder == null) {
                    Log.e(LOG_TAG, "setBackgroundImage after reelase!!");
                    return;
                }

                if (bmp == null) {
                    if (mBackgroundRenderer != null)
                        mBackgroundRenderer.release();
                    mBackgroundRenderer = null;
                    if (mBackgroundTexture != 0)
                        GLES20.glDeleteTextures(1, new int[]{mBackgroundTexture}, 0);
                    mBackgroundTexture = 0;
                    if (callback != null)
                        callback.setBackgroundImageOK();
                    return;
                }

                if (mBackgroundTexture != 0) {
                    GLES20.glDeleteTextures(1, new int[]{mBackgroundTexture}, 0);
                }

                mBackgroundTexture = Common.genNormalTextureID(bmp, GLES20.GL_NEAREST, GLES20.GL_CLAMP_TO_EDGE);

                if (mBackgroundRenderer == null) {
                    mBackgroundRenderer = TextureRendererDrawOrigin.create(false);
                    if (mBackgroundRenderer != null)
                        mBackgroundRenderer.setFlipscale(1.0f, -1.0f);
                }

                if (shouldRecycle)
                    bmp.recycle();

                if (callback != null)
                    callback.setBackgroundImageOK();
            }
        });
    }

    //定制一些初始化操作
    public void setOnCreateCallback(final OnCreateCallback callback) {

        assert callback != null : "无意义操作!";

        if (mFrameRecorder == null) {
            mOnCreateCallback = callback;
        } else {
            // 已经创建完毕， 直接执行
            queueEvent(new Runnable() {
                @Override
                public void run() {
                    callback.createOver(cameraInstance().getCameraDevice() != null);
                }
            });
        }
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        Log.i(LOG_TAG, "onSurfaceCreated...");

        GLES20.glDisable(GLES20.GL_DEPTH_TEST);
        GLES20.glDisable(GLES20.GL_STENCIL_TEST);
        GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);

        int texSize[] = new int[1];

        GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, texSize, 0);
        maxTextureSize = texSize[0];

        mTextureID = Common.genSurfaceTextureID();
        mSurfaceTexture = new SurfaceTexture(mTextureID);
        mSurfaceTexture.setOnFrameAvailableListener(this);

        mFrameRecorder = new CGEFrameRecorder();
        if (!mFrameRecorder.init(mRecordWidth, mRecordHeight, mRecordWidth, mRecordHeight)) {
            Log.e(LOG_TAG, "Frame Recorder init failed!");
        }

        mFrameRecorder.setSrcRotation((float) (Math.PI / 2.0));
        mFrameRecorder.setSrcFlipScale(1.0f, -1.0f);
        mFrameRecorder.setRenderFlipScale(1.0f, -1.0f);

        requestRender();

        if (!cameraInstance().isCameraOpened()) {

            int facing = mIsCameraBackForward ? Camera.CameraInfo.CAMERA_FACING_BACK : Camera.CameraInfo.CAMERA_FACING_FRONT;

            if (!cameraInstance().tryOpenCamera(cameraOpenCallback, facing)) {
                Log.e(LOG_TAG, "相机启动失败!!");
            }
        }

        if (mOnCreateCallback != null) {
            mOnCreateCallback.createOver(cameraInstance().getCameraDevice() != null);
        }
    }

    private void initCamera() {
        int facing = mIsCameraBackForward ? Camera.CameraInfo.CAMERA_FACING_BACK : Camera.CameraInfo.CAMERA_FACING_FRONT;

        boolean openCameraFlag = cameraInstance().tryOpenCamera(new CameraInstance.CameraOpenCallback() {
            @Override
            public void cameraReady() {
                Log.i(LOG_TAG, "重新打开摄像头 OK...");

                if (cameraInstance().isPreviewing()) {
                    cameraInstance().stopPreview();
                }

                if (!cameraInstance().isPreviewing()) {
                    cameraInstance().startPreview(mSurfaceTexture);
                }

                onResume();

                if (cameraOpenCallback != null) {
                    cameraOpenCallback.cameraReady();
                }
            }
        }, facing);

        if (!openCameraFlag) {
            tryOpenCameraAgain();
        }
    }

    public void tryOpenCameraAgain() {
        Log.i(LOG_TAG, "尝试重新打开摄像头");
        driveVideoHandler.sendEmptyMessageDelayed(INIT_CAMERA, 2 * 1000);
    }

    protected void calcViewport() {

        float scaling;

        if (mIsUsingMask) {
            scaling = mMaskAspectRatio;
        } else {
            scaling = mRecordWidth / (float) mRecordHeight;
        }

        float viewRatio = viewWidth / (float) viewHeight;
        float s = scaling / viewRatio;

        int w, h;

        if (mFitFullView) {
            //撑满全部view(内容大于view)
            if (s > 1.0) {
                w = (int) (viewHeight * scaling);
                h = viewHeight;
            } else {
                w = viewWidth;
                h = (int) (viewWidth / scaling);
            }
        } else {
            //显示全部内容(内容小于view)
            if (s > 1.0) {
                w = viewWidth;
                h = (int) (viewWidth / scaling);
            } else {
                h = viewHeight;
                w = (int) (viewHeight * scaling);
            }
        }

        mDrawViewport.width = w;
        mDrawViewport.height = h;
        mDrawViewport.x = (viewWidth - mDrawViewport.width) / 2;
        mDrawViewport.y = (viewHeight - mDrawViewport.height) / 2;
        Log.i(LOG_TAG, String.format("View port: %d, %d, %d, %d", mDrawViewport.x, mDrawViewport.y, mDrawViewport.width, mDrawViewport.height));
    }

    public synchronized void release(final ReleaseOKCallback callback) {

        if (mFrameRecorder != null) {

            queueEvent(new Runnable() {
                @Override
                public void run() {

                    if (mFrameRecorder != null) {
                        mFrameRecorder.release();
                        mFrameRecorder = null;
                        GLES20.glDeleteTextures(1, new int[]{mTextureID}, 0);
                        mTextureID = 0;
                        mSurfaceTexture.release();
                        mSurfaceTexture = null;
                        if (mBackgroundRenderer != null) {
                            mBackgroundRenderer.release();
                            mBackgroundRenderer = null;
                        }
                        if (mBackgroundTexture != 0) {
                            GLES20.glDeleteTextures(1, new int[]{mBackgroundTexture}, 0);
                            mBackgroundTexture = 0;
                        }

                        Log.i(LOG_TAG, "GLSurfaceview release...");
                        if (callback != null)
                            callback.releaseOK();
                    }
                }
            });
        }
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        Log.i(LOG_TAG, String.format("onSurfaceChanged: %d x %d", width, height));

        GLES20.glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);

        viewWidth = width;
        viewHeight = height;

        calcViewport();

        if (cameraInstance().isPreviewing()) {
            cameraInstance().stopPreview();
        }

        if (!cameraInstance().isPreviewing()) {
            cameraInstance().startPreview(mSurfaceTexture);
            mFrameRecorder.srcResize(cameraInstance().previewWidth(), cameraInstance().previewHeight());
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        super.surfaceDestroyed(holder);
        cameraInstance().getCameraDevice().setPreviewCallback(null);
        cameraInstance().stopCamera();
        Log.i(LOG_TAG, "surfaceDestroyed---------");
    }

    public void stopPreview() {

//        stopDetectingFace();

        queueEvent(new Runnable() {
            @Override
            public void run() {
                cameraInstance().stopPreview();
            }
        });
    }

    public synchronized void resumePreview() {

        if (mFrameRecorder == null) {
            Log.e(LOG_TAG, "resumePreview after release!!");
            return;
        }

        if (!cameraInstance().isCameraOpened()) {

            int facing = mIsCameraBackForward ? Camera.CameraInfo.CAMERA_FACING_BACK : Camera.CameraInfo.CAMERA_FACING_FRONT;


            boolean openCameraFlag = cameraInstance().tryOpenCamera(new CameraInstance.CameraOpenCallback() {
                @Override
                public void cameraReady() {
                    Log.i(LOG_TAG, "tryOpenCamera OK...");

                    if (cameraOpenCallback != null) {
                        cameraOpenCallback.cameraReady();
                    }
                }
            }, facing);
            if (openCameraFlag == false) {

            }
        }

        if (!cameraInstance().isPreviewing()) {
            cameraInstance().startPreview(mSurfaceTexture);
            mFrameRecorder.srcResize(cameraInstance().previewWidth(), cameraInstance().previewHeight());
        }

//        resumeDetectingFace();
        requestRender();
    }

    @Override
    public void onDrawFrame(GL10 gl) {

        if (mSurfaceTexture == null || !cameraInstance().isPreviewing()) {
            //防止双缓冲情况下最后几帧抖动
            if (mFrameRecorder != null) {
                GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
                GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
                mFrameRecorder.render(mDrawViewport.x, mDrawViewport.y, mDrawViewport.width, mDrawViewport.height);
            }

            return;
        }

        mSurfaceTexture.updateTexImage();

        mSurfaceTexture.getTransformMatrix(mTransformMatrix);
        mFrameRecorder.update(mTextureID, mTransformMatrix);

        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

        if (mTakeThunbnailCallback != null && !mTakeThunbnailCallback.isUsingBitmap()) {

            synchronized (mThunbnailLock) {

                // double judgement for mTakeThunbnailCallback ensure both performance and safety
                if (mTakeThunbnailCallback != null) {

                    if (mThumnailClipingArea != null) {
                        int clipW = (int) (mThunbnailWidth / mThumnailClipingArea.width());
                        int clipH = (int) (mThunbnailHeight / mThumnailClipingArea.height());
                        int x = -(int) (clipW * mThumnailClipingArea.left);
                        int y = -(int) (clipH * mThumnailClipingArea.top);

                        GLES20.glViewport(x, y, clipW, clipH);

                    } else {
                        GLES20.glViewport(0, 0, mThunbnailWidth, mThunbnailHeight);
                    }

                    mFrameRecorder.drawCache();

                    mThunbnailBuffer.position(0);
                    GLES20.glReadPixels(0, 0, mThunbnailWidth, mThunbnailHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mThunbnailBuffer);

                    mThunbnailBmp.copyPixelsFromBuffer(mThunbnailBuffer);

                    post(new Runnable() {
                        @Override
                        public void run() {
                            synchronized (mThunbnailLock) {
                                if (mTakeThunbnailCallback != null)
                                    mTakeThunbnailCallback.takeThunbnailOK(mThunbnailBmp);
                            }
                        }
                    });
                }
            }
        }

        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        if (mBackgroundRenderer != null) {
            GLES20.glViewport(0, 0, viewWidth, viewHeight);
            mBackgroundRenderer.renderTexture(mBackgroundTexture, null);
        }
        GLES20.glEnable(GLES20.GL_BLEND);
        mFrameRecorder.render(mDrawViewport.x, mDrawViewport.y, mDrawViewport.width, mDrawViewport.height);
        GLES20.glDisable(GLES20.GL_BLEND);
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.i(LOG_TAG, "glsurfaceview onResume...");
    }

    @Override
    public void onPause() {
        Log.i(LOG_TAG, "glsurfaceview onPause in...");

//        cameraInstance().stopCamera();
        super.onPause();
        Log.i(LOG_TAG, "glsurfaceview onPause out...");
    }

    @Override
    public void onFrameAvailable(SurfaceTexture surfaceTexture) {

        requestRender();

        if (mLastTimestamp2 == 0)
            mLastTimestamp2 = System.currentTimeMillis();

        long currentTimestamp = System.currentTimeMillis();

        ++mFramesCount2;
        mTimeCount2 += currentTimestamp - mLastTimestamp2;
        mLastTimestamp2 = currentTimestamp;
        if (mTimeCount2 >= 1000) {
//            Log.i(LOG_TAG, String.format("相机每秒采样率: %d", mFramesCount2));
            mTimeCount2 %= 1000;
            mFramesCount2 = 0;
        }
    }

    public boolean isTakingThunbnail() {
        boolean status;
        synchronized (mThunbnailLock) {
            status = mTakeThunbnailCallback != null;
        }
        return status;
    }

    public void startThunbnailCliping(Bitmap cache, RectF clipingArea, TakeThunbnailCallback callback) {
        synchronized (mThunbnailLock) {
            mTakeThunbnailCallback = callback;
            mThunbnailBmp = cache;
            mThunbnailWidth = cache.getWidth();
            mThunbnailHeight = cache.getHeight();
            mThunbnailBuffer = IntBuffer.allocate(mThunbnailWidth * mThunbnailHeight);
            mThumnailClipingArea = clipingArea;
        }
    }

    public void startThunbnailCliping(int width, int height, RectF clipingArea, TakeThunbnailCallback callback) {
        synchronized (mThunbnailLock) {
            mTakeThunbnailCallback = callback;
            mThunbnailBmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            mThunbnailWidth = width;
            mThunbnailHeight = height;
            mThunbnailBuffer = IntBuffer.allocate(width * height);
            mThumnailClipingArea = clipingArea;
        }
    }

    public void stopThunbnailCliping() {
        synchronized (mThunbnailLock) {
            mTakeThunbnailCallback = null;
            mThunbnailBmp.recycle();
            mThunbnailBmp = null;
            mThunbnailBuffer = null;
            mThumnailClipingArea = null;
        }
    }

    public void takeShot(final TakePictureCallback callback) {
        takeShot(callback, true);
    }

    public synchronized void takeShot(final TakePictureCallback callback, final boolean noMask) {
        assert callback != null : "callback must not be null!";

        if (mFrameRecorder == null) {
            Log.e(LOG_TAG, "Recorder not initialized!");
            callback.takePictureOK(null);
            return;
        }

        queueEvent(new Runnable() {
            @Override
            public void run() {

                FrameBufferObject frameBufferObject = new FrameBufferObject();
                int bufferTexID;
                IntBuffer buffer;
                Bitmap bmp;

                if (noMask || !mIsUsingMask) {

                    bufferTexID = Common.genBlankTextureID(mRecordWidth, mRecordHeight);
                    frameBufferObject.bindTexture(bufferTexID);
                    GLES20.glViewport(0, 0, mRecordWidth, mRecordHeight);
                    mFrameRecorder.drawCache();
                    buffer = IntBuffer.allocate(mRecordWidth * mRecordHeight);
                    GLES20.glReadPixels(0, 0, mRecordWidth, mRecordHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buffer);
                    bmp = Bitmap.createBitmap(mRecordWidth, mRecordHeight, Bitmap.Config.ARGB_8888);
                    bmp.copyPixelsFromBuffer(buffer);
                    Log.i(LOG_TAG, String.format("w: %d, h: %d", mRecordWidth, mRecordHeight));

                } else {

                    bufferTexID = Common.genBlankTextureID(mDrawViewport.width, mDrawViewport.height);
                    frameBufferObject.bindTexture(bufferTexID);

                    int w = Math.min(mDrawViewport.width, viewWidth);
                    int h = Math.min(mDrawViewport.height, viewHeight);

                    mFrameRecorder.setRenderFlipScale(1.0f, 1.0f);
                    mFrameRecorder.setMaskTextureRatio(mMaskAspectRatio);
                    mFrameRecorder.render(0, 0, w, h);
                    mFrameRecorder.setRenderFlipScale(1.0f, -1.0f);
                    mFrameRecorder.setMaskTextureRatio(mMaskAspectRatio);

                    Log.i(LOG_TAG, String.format("w: %d, h: %d", w, h));
                    buffer = IntBuffer.allocate(w * h);
                    GLES20.glReadPixels(0, 0, w, h, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buffer);
                    bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
                    bmp.copyPixelsFromBuffer(buffer);
                }

                frameBufferObject.release();
                GLES20.glDeleteTextures(1, new int[]{bufferTexID}, 0);

                callback.takePictureOK(bmp);
            }
        });

    }

    //isBigger 为true 表示当宽高不满足时，取最近的较大值.
    // 若为 false 则取较小的
    public void setPictureSize(int width, int height, boolean isBigger) {
        //默认会旋转90度.
        cameraInstance().setPictureSize(height, width, isBigger);
    }

    public synchronized void takePicture(final TakePictureCallback photoCallback, Camera.ShutterCallback shutterCallback, final String config, final float intensity, final boolean isFrontMirror) {

        Camera.Parameters params = cameraInstance().getParams();

        if (photoCallback == null || params == null) {
            Log.e(LOG_TAG, "takePicture after release!");
            if (photoCallback != null) {
                photoCallback.takePictureOK(null);
            }
            return;
        }

        try {
            params.setRotation(90);
            cameraInstance().setParams(params);
        } catch (Exception e) {
            Log.e(LOG_TAG, "Error when takePicture: " + e.toString());
            if (photoCallback != null) {
                photoCallback.takePictureOK(null);
            }
            return;
        }

        cameraInstance().getCameraDevice().takePicture(shutterCallback, null, new Camera.PictureCallback() {
            @Override
            public void onPictureTaken(final byte[] data, Camera camera) {

                Camera.Parameters params = camera.getParameters();
                Camera.Size sz = params.getPictureSize();

                boolean shouldRotate;

                Bitmap bmp;
                int width, height;

                //当拍出相片不为正方形时， 可以判断图片是否旋转
                if (sz.width != sz.height) {
                    //默认数据格式已经设置为 JPEG
                    bmp = BitmapFactory.decodeByteArray(data, 0, data.length);
                    width = bmp.getWidth();
                    height = bmp.getHeight();
                    shouldRotate = (sz.width > sz.height && width > height) || (sz.width < sz.height && width < height);
                } else {
                    Log.i(LOG_TAG, "Cache image to get exif.");

                    try {
                        String tmpFilename = mContext.getExternalCacheDir() + "/picture_cache000.jpg";
                        FileOutputStream fileout = new FileOutputStream(tmpFilename);
                        BufferedOutputStream bufferOutStream = new BufferedOutputStream(fileout);
                        bufferOutStream.write(data);
                        bufferOutStream.flush();
                        bufferOutStream.close();

                        ExifInterface exifInterface = new ExifInterface(tmpFilename);
                        int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

                        switch (orientation) {
                            //被保存图片exif记录只有旋转90度， 和不旋转两种情况
                            case ExifInterface.ORIENTATION_ROTATE_90:
                                shouldRotate = true;
                                break;
                            default:
                                shouldRotate = false;
                                break;
                        }

                        bmp = BitmapFactory.decodeFile(tmpFilename);
                        width = bmp.getWidth();
                        height = bmp.getHeight();

                    } catch (IOException e) {
                        Log.e(LOG_TAG, "Err when saving bitmap...");
                        e.printStackTrace();
                        return;
                    }
                }


                if (width > maxTextureSize || height > maxTextureSize) {
                    float scaling = Math.max(width / (float) maxTextureSize, height / (float) maxTextureSize);
                    Log.i(LOG_TAG, String.format("目标尺寸(%d x %d)超过当前设备OpenGL 能够处理的最大范围(%d x %d)， 现在将图片压缩至合理大小!", width, height, maxTextureSize, maxTextureSize));

                    bmp = Bitmap.createScaledBitmap(bmp, (int) (width / scaling), (int) (height / scaling), false);

                    width = bmp.getWidth();
                    height = bmp.getHeight();
                }

                Bitmap bmp2;

                if (shouldRotate) {
                    bmp2 = Bitmap.createBitmap(height, width, Bitmap.Config.ARGB_8888);

                    Canvas canvas = new Canvas(bmp2);

                    if (cameraInstance().getFacing() == Camera.CameraInfo.CAMERA_FACING_BACK) {
                        Matrix mat = new Matrix();
                        int halfLen = Math.min(width, height) / 2;
                        mat.setRotate(90, halfLen, halfLen);
                        canvas.drawBitmap(bmp, mat, null);
                    } else {
                        Matrix mat = new Matrix();

                        if (isFrontMirror) {
                            mat.postTranslate(-width / 2, -height / 2);
                            mat.postScale(-1.0f, 1.0f);
                            mat.postTranslate(width / 2, height / 2);
                            int halfLen = Math.min(width, height) / 2;
                            mat.postRotate(90, halfLen, halfLen);
                        } else {
                            int halfLen = Math.max(width, height) / 2;
                            mat.postRotate(-90, halfLen, halfLen);
                        }

                        canvas.drawBitmap(bmp, mat, null);
                    }

                    bmp.recycle();
                } else {
                    if (cameraInstance().getFacing() == Camera.CameraInfo.CAMERA_FACING_BACK) {
                        bmp2 = bmp;
                    } else {

                        bmp2 = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
                        Canvas canvas = new Canvas(bmp2);
                        Matrix mat = new Matrix();
                        if (isFrontMirror) {
                            mat.postTranslate(-width / 2, -height / 2);
                            mat.postScale(1.0f, -1.0f);
                            mat.postTranslate(width / 2, height / 2);
                        } else {
                            mat.postTranslate(-width / 2, -height / 2);
                            mat.postScale(-1.0f, -1.0f);
                            mat.postTranslate(width / 2, height / 2);
                        }

                        canvas.drawBitmap(bmp, mat, null);
                    }

                }

                if (config != null) {
                    CGENativeLibrary.filterImage_MultipleEffectsWriteBack(bmp2, config, intensity);
                }

                photoCallback.takePictureOK(bmp2);

                cameraInstance().getCameraDevice().startPreview();
//                resumeDetectingFace();
            }
        });
    }

    public interface SetMaskBitmapCallback {
        void setMaskOK(CGEFrameRecorder recorder);
    }

    public interface SetBackgroundImageCallback {
        void setBackgroundImageOK();
    }

    public interface OnCreateCallback {
        void createOver(boolean success);
    }

    public interface ReleaseOKCallback {

        void releaseOK();
    }

    public interface TakeThunbnailCallback {
        // 当 TakeThunbnailCallback 被设置之后
        // 每一帧都会获取 isUsingBitmap() 返回值
        // 当 isUsingBitmap() 返回 true 的时候 takeThunbnailOK 将不被调用
        // 当 isUsingBitmap() 返回 false 的时候 takeThunbnailOK 正常执行
        boolean isUsingBitmap();

        void takeThunbnailOK(Bitmap bmp);
    }

    public interface TakePictureCallback {
        //传入的bmp可以由接收者recycle
        void takePictureOK(Bitmap bmp);
    }

    public class ClearColor {
        public float r, g, b, a;
    }

    private class DriveVideoHandler extends Handler {

        public DriveVideoHandler(/*Looper looper*/) {
            super(Looper.getMainLooper());
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case INIT_CAMERA:
                    if (!cameraInstance().isCameraOpened()) {
                        initCamera();
                    }
                    break;
            }
        }
    }
}
